import ImCommon
import UIKit

// 客户端
class Client {
    private var mFriendCount: Int       // 好友数
    private var mThread: Thread?        // 接收数据的线程
    private var mSocket: Int32          // 套接字
    private static var sIconIndex = 0   // 头像索引
    private static var sUsername = ""   // 用户名

    /*
     * 构造方法。
     */
    public init() {
        // 初始化成员
        mFriendCount = 0
        mSocket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)

        // 连接服务器
        var addr = sockaddr_in()
        memset(&addr, 0, MemoryLayout<sockaddr_in>.size)
        addr.sin_len = __uint8_t(MemoryLayout<sockaddr_in>.size)
        addr.sin_family = sa_family_t(AF_INET)
        addr.sin_port = _OSSwapInt16(ImProtocol.SERVER_PORT)
        addr.sin_addr.s_addr = inet_addr(ImProtocol.SERVER_IP)
        var serverAddr = sockaddr()
        memcpy(&serverAddr, &addr, MemoryLayout<sockaddr_in>.size)
        let result = connect(mSocket, &serverAddr, socklen_t(MemoryLayout<sockaddr_in>.size)) == 0
        if result {
            // 开启接收数据的线程
            mThread = Thread(target: self, selector: #selector(receiveData), object: nil)
            mThread?.start()
        } else {
            // 发送通知
            DispatchQueue.main.async {
                NotificationCenter.default.post(name: Const.UM_NETWORK_ERROR, object: result)
            }
        }
    }

    /**
     * 添加好友。
     * @param friendName  好友名字
     * @return 结果
     */
    public func addFriend(_ friendName: String) -> ResultCode {
        // 检查是否超过好友上限
        if mFriendCount > Const.MAX_USER_COUNT {
            return .TOO_MANY_FRIENDS
        }
        
        // 检查好友名
        if friendName == Client.sUsername {
            return .FRIEND_NAME_EQUAL_SELF_NAME
        }
    
        // 发送添加好友请求
        let request = AddFriendRequest(friendName, srcIconIndex: Client.sIconIndex, srcUsername: Client.sUsername)
        self.sendData(request)
        
        return ResultCode.SUCCEEDED
    }
    
    /**
     * 允许或拒绝添加好友。
     * @param allow     允许则为 true，否则为 false
     * @param request   添加好友请求
     */
    public func allowAddFriend(_ allow: Bool, request: AddFriendRequest) {
        // 发送结果
        let addFriendResult = AddFriendResult(Client.sIconIndex, dstUsername: request.mDstUsername, 
            resultCode: allow ? ResultCode.ADD_FRIEND_ALLOW: ResultCode.ADD_FRIEND_NOT_ALLOW, srcUsername: request.mSrcUsername)
        self.sendData(addFriendResult)

        // 如果允许，则好友数加1
        if allow {
            mFriendCount += 1
        }
    }
    
    /**
     * 聊天。
     * @param chatInfo  聊天信息
     */
    public func chat(_ chatInfo: ChatInfo) {
        self.sendData(chatInfo)
    }

    /**
     * 关闭客户端。
     */
    public func close() {
        // 离线
        self.logoff()
        
        // 延迟一下，防止数据还没发送完成就关闭网络
        Thread.sleep(forTimeInterval: 0.01)
        
        // 停止线程
        mThread?.cancel()
        
        // 关闭
        Darwin.close(mSocket)
    }

    /**
     * 获取好友数。
     */
    public var friendCount: Int {
        get {
            return mFriendCount
        }
    }

    /**
     * 处理添加好友请求。
     * @param request   添加好友请求
     */
    private func handleAddFriendRequest(_ request: AddFriendRequest) {
        DispatchQueue.main.async {
            NotificationCenter.default.post(name: Const.UM_GET_ADD_FRIEND_REQUEST, object: request)
        }
    }

    /**
     * 处理添加好友结果。
     * @param result    添加好友结果
     */
    private func handleAddFriendResult(_ result: AddFriendResult) {
        if result.mResultCode == .ADD_FRIEND_ALLOW && result.mDstIconIndex != -1 {
            mFriendCount += 1
        }

        DispatchQueue.main.async {
            NotificationCenter.default.post(name: Const.UM_GET_ADD_FRIEND_RESULT, object: result)
        }
    }
 
    /**
     * 处理聊天。
     * @param chatInfo  聊天信息
     */
    private func handleChat(_ chatInfo: ChatInfo) {
        DispatchQueue.main.async {
            NotificationCenter.default.post(name: Const.UM_CHAT_S2C, object: chatInfo)
        }
    }

    /**
     * 处理其他用户的离线请求。
     * @param request   其他用户的离线请求
     */
    private func handleFriendLogoff(_ request: LogoffRequest) {
        DispatchQueue.main.async {
            NotificationCenter.default.post(name: Const.UM_FRIEND_LOGOFF, object: request)
        }
    }

    /**
     * 处理其他用户的登录消息。
     * @param friendLogon   其他用户的登录消息
     */
    private func handleFriendLogon(_ friendLogon: FriendLogon) {
        DispatchQueue.main.async {
            NotificationCenter.default.post(name: Const.UM_FRIEND_LOGON, object: friendLogon)
        }
    }
    
    /**
     * 处理登录结果。
     * @param result    登录结果
     */
    private func handleLogonResult(_ result: LogonResult) {
        DispatchQueue.main.async {
            NotificationCenter.default.post(name: Const.UM_GET_LOGON_RESULT, object: result)
        }

        if result.mResultCode == .LOGON_SUCCEEDED {
            Client.sIconIndex = result.mIconIndex
            mFriendCount = result.mFriendCount
        } else {
            Client.sUsername = ""
        }
    }

    /**
     * 处理注册结果。
     * @param result    注册结果
     */
    private func handleRegResult(_ result: RegResult) {
        DispatchQueue.main.async {
            NotificationCenter.default.post(name: Const.UM_GET_REG_RESULT, object: result)
        }

        if result.mResultCode != .REG_SUCCEEDED {
            Client.sUsername = ""
        }
    }

    /**
     * 获取自己头像索引。
     */
    public static var iconIndex: Int {
        get {
            return sIconIndex
        }
    }
    
    /**
     * 离线。
     */
    public func logoff() {
        if Client.sUsername == "" {
            return;
        }

        let request = LogoffRequest(Client.sUsername)
        self.sendData(request)

        Client.sUsername = ""
    }

    /**
     * 登录。
     */
    public func logon(_ username: String, password: String) {
        // 发送登录请求
        let request = LogonRequest(password, username: username)
        self.sendData(request)

        // 保存用户名
        Client.sUsername = username
    }

    /**
     * 处理服务器发送来的数据。
     * @param pro   协议
     */
    public func onGetMessageFromServer(_ pro: ImProtocol) {
        switch pro.mType {
            case .ADD_FRIEND_REQUEST:
                self.handleAddFriendRequest(pro as! AddFriendRequest)
            case .ADD_FRIEND_RESULT:
                self.handleAddFriendResult(pro as! AddFriendResult)
            case .CHAT:
                self.handleChat(pro as! ChatInfo)
            case .FRIEND_LOGON:
                self.handleFriendLogon(pro as! FriendLogon)
            case .LOGOFF:
                self.handleFriendLogoff(pro as! LogoffRequest)
            case .LOGON:
                self.handleLogonResult(pro as! LogonResult)
            case .REG:
                self.handleRegResult(pro as! RegResult)
            case .UNKNOWN:
                break
        }
    }
    
    /**
     * 接收数据。
     */
    @objc public func receiveData() {
        while true {
            // 接收数据长度
            var sizeBuffer = [UInt8](repeating: 0, count: MemoryLayout<Int>.size)
            var read = recv(mSocket, &sizeBuffer, MemoryLayout<Int>.size, 0)
            if read <= 0 {
                break
            }
            var dataSize = 0
            memcpy(&dataSize, &sizeBuffer, MemoryLayout<Int>.size)
            
            // 接收数据
            var proBuffer = [UInt8](repeating: 0, count: dataSize)
            read = recv(mSocket, &proBuffer, dataSize, 0)
            if read <= 0 {
                break
            }

            // 将数据转换成协议
            let pro = ImProtocol.unArchive(proBuffer, count: dataSize)

            // 处理协议
            self.onGetMessageFromServer(pro)
        }
    }
    
    /**
     * 注册。
     * @param username  用户名
     * @param password  密码
     * @param iconIndex 头像索引
     */
    public func reg(_ username: String, password: String, iconIndex: Int) {
        // 发送注册请求
        let request = RegRequest(iconIndex, password: password, username: username)
        self.sendData(request)
        
        // 保存用户名和头像索引
        Client.sUsername = username
        Client.sIconIndex = iconIndex
    }
    
    /**
     * 发送数据。
     * @param pro   协议
     */
    private func sendData(_ pro: ImProtocol) {
        // 将协议转换成数据
        let data = ImProtocol.archive(pro)
        
        // 发送数据长度
        var dataSize = data.count
        send(mSocket, &dataSize, MemoryLayout<Int>.size, 0)
        
        // 发送数据
        send(mSocket, data, data.count, 0)
        
        // 防止发送过快导致服务器无法响应
        Thread.sleep(forTimeInterval: 0.01)
    }
    
    /**
     * 获取自己的名字。
     */
    public static var username: String {
        get {
            return sUsername
        }
        set {
            sUsername = newValue
        }
    }
}
